After having run the iLand simulations for the 12 clusters the resulting .sqlite files need to be evaluated. Goal is to see how the tree development of each cluster develops.

1. Load data from .sqlite files

To be able to load the data, fist a connection to the .sqlite file needs to be established. For this the library “RSQLite” is needed.

library(rstatix)

Attaching package: ‘rstatix’

The following object is masked from ‘package:stats’:

    filter

With the necessary libraries loaded, the data can be assessed. First a connection to the database is established, afterwards the tables inside can be read.

setwd("rawData/iLand/iLand_output")
Warning: The working directory was changed to C:/Users/ge45lep/Documents/2023_PanEuropean/r_paneurop/rawData/iLand/iLand_output inside a notebook chunk. The working directory will be reset when the chunk is finished running. Use the knitr root.dir option in the setup chunk to change the working directory for notebook chunks.
getwd()
[1] "C:/Users/ge45lep/Documents/2023_PanEuropean/r_paneurop/rawData/iLand/iLand_output"
# Get output dirs and run through them
lDirs <- list.dirs(recursive = FALSE)
lDirs <- grep("_new", lDirs, value = TRUE)
DBTables <- list()
DBT_sap <- list()
DBT_sapd <- list()
iFiles <- 1
iDirs <- 1
bExtSeed <- FALSE

# Outer loop runs through all directories at that path and loads .sqlite files
# into a list. Also a counter (iDirs) counts the number of directories accessed
for (l_dir in lDirs) {
  lFiles <- list.files(path = l_dir, pattern = ".sqlite", full.names = TRUE)
  
  # Inner loop runs through all files (.sqlite) in a directory and loads the
  # stand table into a variable.
  # File for cluster 3_2 is skipped, due to this stand being empty in run 
  # without external seeeds
  for (lFile in lFiles) {
    l_db <- dbConnect(RSQLite::SQLite(), lFile)
    l_dfStnd <- dbReadTable(l_db, "stand")
    l_dfSapl <- dbReadTable(l_db, "sapling")
    l_dfSapD <- dbReadTable(l_db, "saplingdetail")
    dbDisconnect(l_db)
    
    bExtSeed <- !grepl("noseed", lFile)

    if (nrow(l_dfStnd) == 0) {
      next
    }
    
    l_dfStnd$filenm <- tools::file_path_sans_ext(basename(lFile))
    l_dfStnd <- l_dfStnd %>%
      dplyr::select(filenm,
                    year,
                    species,
                    volume_m3,
                    dbh_avg_cm,
                    basal_area_m2,
                    count_ha,
                    cohort_count_ha,
                    cohort_basal_area)
    l_dfStnd$run_nr <- iDirs
    l_dfStnd$external_seed <- bExtSeed
    
    l_dfSapl$filenm <- tools::file_path_sans_ext(basename(lFile))
    l_dfSapD$filenm <- tools::file_path_sans_ext(basename(lFile))
    
    DBTables[[iFiles]] <- l_dfStnd
    DBT_sap[[iFiles]] <- l_dfSapl
    DBT_sapd[[iFiles]] <- l_dfSapD
    
    iFiles <- iFiles + 1
    #dbDisconnect(l_db)
  }
  iDirs <- iDirs + 1
}
# Bring all the data together in big_data variable
OG_big_data <- bind_rows(DBTables)
OG_sapl <- bind_rows(DBT_sap)
OG_sapd <- bind_rows(DBT_sapd)

# Cleanup of some variables
rm(DBTables, l_db, l_dfStnd, iDirs, iFiles, l_dir, lDirs, lFile, lFiles, 
   bExtSeed, DBT_sapd, DBT_sap, l_dfSapl, l_dfSapD)

2. Data handling

For further processing it might be usefull to split the filename into a few pieces:

# 1. stand table
big_data <- OG_big_data |>
  separate_wider_delim(filenm, delim = "_", names = c("clim_model",
                                                      "clim_scenario",
                                                      "env_clst",
                                                      "stnd_clst",
                                                      "ext_seed"))
big_data <- big_data %>% dplyr::select(!ext_seed)
big_data$cluster <- paste(big_data$env_clst, big_data$stnd_clst, sep = "_")
big_data <- big_data %>%
  dplyr::select(!c(env_clst, stnd_clst))
big_data$clim_scenario <- as.factor(big_data$clim_scenario)
big_data$clim_model <- as.factor(big_data$clim_model)
big_data$cluster <- as.factor(big_data$cluster)
big_data$species <- as.factor(big_data$species)

strCols <- c("volume_m3", "dbh_avg_cm", "basal_area_m2",
             "count_ha", "cohort_count_ha", "cohort_basal_area")

w_means <- big_data %>%
  dplyr::group_by(clim_scenario, external_seed, cluster, year, species) %>%
  dplyr::summarise(across(strCols, list(mean = mean, sd = sd)),.groups = "drop")
Warning: There was 1 warning in `dplyr::summarise()`.
i In argument: `across(strCols, list(mean = mean, sd = sd))`.
Caused by warning:
! Using an external vector in selections was deprecated in tidyselect 1.1.0.
i Please use `all_of()` or `any_of()` instead.
  # Was:
  data %>% select(strCols)

  # Now:
  data %>% select(all_of(strCols))

See <https://tidyselect.r-lib.org/reference/faq-external-vector.html>.
This warning is displayed once every 8 hours.
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.
# 2. sapling table
big_sapl <- OG_sapl |>
  separate_wider_delim(filenm, delim = "_", names = c("clim_model",
                                                      "clim_scenario",
                                                      "env_clst",
                                                      "stnd_clst",
                                                      "ext_seed"))
big_sapl$cluster <- paste(big_sapl$env_clst, big_sapl$stnd_clst, sep = "_")
big_sapl$clim_model <- as.factor(big_sapl$clim_model)
big_sapl$clim_scenario <- as.factor(big_sapl$clim_scenario)
big_sapl$cluster <- as.factor(big_sapl$cluster)
big_sapl$species <- as.factor(big_sapl$species)
big_sapl$ext_seed <- as.factor(big_sapl$ext_seed)
big_sapl <- big_sapl %>% dplyr::select(!c(env_clst, stnd_clst, ru, rid))

strCols <- c("count_ha", "count_small_ha", "cohort_count_ha", "height_avg_m",
             "age_avg", "LAI")

w_means_sapl <- big_sapl %>%
  dplyr::group_by(clim_scenario, ext_seed, cluster, year, species) %>%
  dplyr::summarise(across(strCols, list(mean = mean, sd = sd)),.groups = "drop")

# 3. saplingdetails table
big_sapD <- OG_sapd |>
  separate_wider_delim(filenm, delim = "_", names = c("clim_model",
                                                      "clim_scenario",
                                                      "env_clst",
                                                      "stnd_clst",
                                                      "ext_seed"))
big_sapD$cluster <- paste(big_sapD$env_clst, big_sapD$stnd_clst, sep = "_")
big_sapD$clim_model <- as.factor(big_sapD$clim_model)
big_sapD$clim_scenario <- as.factor(big_sapD$clim_scenario)
big_sapD$cluster <- as.factor(big_sapD$cluster)
big_sapD$species <- as.factor(big_sapD$species)
big_sapD$ext_seed <- as.factor(big_sapD$ext_seed)
big_sapD <- big_sapD %>% dplyr::select(!c(env_clst, stnd_clst, ru, rid))

strCols <- c("age", "height", "dbh", "n_represented")

w_means_sapD <- big_sapD %>%
  dplyr::group_by(clim_scenario, ext_seed, cluster, year, species) %>%
  dplyr::summarise(across(strCols, list(mean = mean, sd = sd)),.groups = "drop")

# clean environment
rm(strCols)

2.1 Effect strength

Effect strength of all variables that influence the number of trees is calculated to identify which can be included in the mean calculation.

ggplot(w_means, aes(x = count_ha_mean)) + geom_histogram()

ggplot(w_means, aes(x = count_ha_mean)) + geom_density()


# test for varianzhomogeniocity (if p < 0.05 -> no homogeneity of variance)
w_means %>% levene_test(count_ha_mean ~ clim_scenario)  #homogeneity of variance
w_means %>% levene_test(volume_m3_mean ~ clim_scenario)  #homogeneity of variance
w_means %>% levene_test(count_ha_mean ~ external_seed)  #no homogeneity of var
Warning: group coerced to factor.
w_means %>% levene_test(count_ha_mean ~ cluster)        #no homogeneity of var
w_means %>% levene_test(count_ha_mean ~ species)        #no homogeneity of var

# test for effect strength - var.equal = FALSE -> no homogeneity of variance
w_means %>% cohens_d(count_ha_mean ~ clim_scenario, var.equal = TRUE) #negligible effect
w_means %>% cohens_d(volume_m3_mean ~ clim_scenario, var.equal = TRUE) #negligible effect
w_means %>% cohens_d(count_ha_mean ~ external_seed, var.equal = FALSE) #small effect
w_means %>% cohens_d(count_ha_mean ~ cluster, var.equal = FALSE) #no to large effect
w_means %>% cohens_d(count_ha_mean ~ species, var.equal = FALSE) #no to large effect

ggplot(w_means[w_means$year == 30 & w_means$external_seed == FALSE,], 
       aes(x = species,y = count_ha_mean, fill = species)) +
  geom_col(position = "dodge") +
  geom_errorbar(aes(ymin = count_ha_mean - count_ha_sd,
                    ymax = count_ha_mean + count_ha_sd)) +
  facet_grid(cluster ~ clim_scenario, scales = "free") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1)) +
  labs(title = "Count per ha, in Year 30 (no external seed)")

Climate scenarios do not seem to have a huge influence on the dataset and can therefore calculated into the uncertainty.

2.2 Mean over all clim_scenarios

Calculate the mean values over all 4 climate scenarios.

# stand
strCols <- c("volume_m3", "dbh_avg_cm", "basal_area_m2",
             "count_ha", "cohort_count_ha", "cohort_basal_area")

w_means_clim <- big_data %>%
  dplyr::group_by(external_seed, cluster, year, species) %>%
  dplyr::summarise(across(strCols, list(mean = mean, sd = sd)),
                   .groups = "drop")

# saplings
strCols <- c("count_ha", "count_small_ha", "cohort_count_ha",
             "height_avg_m", "age_avg")

w_means_sapl_clim <- big_sapl %>%
  dplyr::group_by(ext_seed, cluster, year, species) %>%
  dplyr::summarise(across(strCols, list(mean = mean, sd = sd)),
                   .groups = "drop")

# sapling details
strCols <- c("n_represented", "dbh", "height", "age")

w_means_sapD_clim <- big_sapD %>%
  dplyr::group_by(ext_seed, cluster, year, species) %>%
  dplyr::summarise(across(strCols, list(mean = mean, sd = sd)),
                   .groups = "drop")

rm(strCols)

3. Plots

  1. Development volume, dbh, basal area, count and cohort count over the simulation extend.
# volume/biomass
ggplot(w_means_clim, aes(x = year, y = volume_m3_mean, col = species)) +
  geom_line(aes(linetype = external_seed)) +
  geom_ribbon(aes(linetype = external_seed,
                  ymin = volume_m3_mean - volume_m3_sd,
                  ymax = volume_m3_mean + volume_m3_sd,
                  fill = species),
                  alpha = 0.05) +
  facet_wrap(~cluster, scales = "free") +
  theme_bw() +
  labs(title = "Development of mean volume(m³)/biomass per cluster")


# dbh
ggplot(w_means_clim, aes(x = year, y = dbh_avg_cm_mean, col = species)) +
  geom_line(aes(linetype = external_seed)) +
  geom_ribbon(aes(linetype = external_seed,
                  ymin = dbh_avg_cm_mean - dbh_avg_cm_sd,
                  ymax = dbh_avg_cm_mean + dbh_avg_cm_sd,
                  fill = species),
                  alpha = 0.05) +
  facet_wrap(~cluster, scales = "free") +
  theme_bw() +
  labs(title = "Development of mean dbh(cm) per cluster")


# basal area
ggplot(w_means_clim, aes(x = year, y = basal_area_m2_mean, col = species)) +
  geom_line(aes(linetype = external_seed)) +
  geom_ribbon(aes(linetype = external_seed,
                  ymin = basal_area_m2_mean - basal_area_m2_sd,
                  ymax = basal_area_m2_mean + basal_area_m2_sd,
                  fill = species),
                  alpha = 0.05) +
  facet_wrap(~cluster, scales = "free") +
  theme_bw() +
  labs(title = "Development of mean basal area (m²) per cluster")


# stem count
ggplot(w_means_clim, aes(x = year, y = count_ha_mean, col = species)) +
  geom_line(aes(linetype = external_seed)) +
  geom_ribbon(aes(linetype = external_seed,
                  ymin = count_ha_mean - count_ha_sd,
                  ymax = count_ha_mean + count_ha_sd,
                  fill = species),
                  alpha = 0.05) +
  facet_wrap(~cluster, scales = "free") +
  theme_bw() +
  labs(title = "Development of mean stem count (n/ha) per cluster")


# cohort count
ggplot(w_means_clim[w_means_clim$external_seed == FALSE,],
       aes(x = year, y = cohort_count_ha_mean, col = species)) +
  geom_line(aes(linetype = external_seed)) +
  geom_ribbon(aes(linetype = external_seed,
                  ymin = cohort_count_ha_mean - cohort_count_ha_sd,
                  ymax = cohort_count_ha_mean + cohort_count_ha_sd,
                  fill = species),
                  alpha = 0.05) +
  facet_wrap(~cluster, scales = "free") +
  theme_bw() +
  labs(title = "Development of mean cohort count (n/ha) per cluster (no seed)")


ggplot(w_means_clim[w_means_clim$external_seed == TRUE,],
       aes(x = year, y = cohort_count_ha_mean, col = species)) +
  geom_line(aes(linetype = external_seed)) +
  geom_ribbon(aes(linetype = external_seed,
                  ymin = cohort_count_ha_mean - cohort_count_ha_sd,
                  ymax = cohort_count_ha_mean + cohort_count_ha_sd,
                  fill = species),
                  alpha = 0.05) +
  facet_wrap(~cluster, scales = "free") +
  theme_bw() +
  labs(title = "Development of mean cohort count (n/ha) per cluster")

  1. Dominant species on each cluster in Year 30
# stem count
ggplot(w_means_clim[w_means_clim$year == 30,],
       aes(x = species, y = count_ha_mean, fill = external_seed)) +
  geom_col(position = "dodge") +
  geom_errorbar(aes(ymin = count_ha_mean - count_ha_sd,
                    ymax = count_ha_mean + count_ha_sd)) +
  facet_wrap(~cluster, scales = "free_y", labeller = label_both) +
  theme_bw() +
  theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1)) +
  geom_text(aes(label = species), vjust = 0.25, check_overlap = TRUE) +
  labs(title = "Species mean stem count per cluster (n/ha) in year 30")

# volume/biomass
ggplot(w_means_clim[w_means_clim$year == 30,],
       aes(x = species, y = volume_m3_mean, fill = external_seed)) +
  geom_col(position = "dodge") +
  geom_errorbar(aes(ymin = volume_m3_mean - volume_m3_sd,
                    ymax = volume_m3_mean + volume_m3_sd)) +
  facet_wrap(~cluster, scales = "free_y", labeller = label_both) +
  theme_bw() +
  theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1)) +
  geom_text(aes(label = species), vjust = 0.25, check_overlap = TRUE) +
  labs(title = "Species mean volume per cluster (m³)/Biomass in year 30")

# DBH
ggplot(w_means_clim[w_means_clim$year == 30,],
       aes(x = species, y = dbh_avg_cm_mean, fill = external_seed)) +
  geom_col(position = "dodge") +
  geom_errorbar(aes(ymin = dbh_avg_cm_mean - dbh_avg_cm_sd,
                    ymax = dbh_avg_cm_mean + dbh_avg_cm_sd)) +
  facet_wrap(~cluster, scales = "free_y", labeller = label_both) +
  theme_bw() +
  theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1)) +
  geom_text(aes(label = species), vjust = 0.25, check_overlap = TRUE) +
  labs(title = "Species mean dbh per cluster (cm) in year 30")

# basal area
ggplot(w_means_clim[w_means_clim$year == 30,],
       aes(x = species, y = basal_area_m2_mean, fill = external_seed)) +
  geom_col(position = "dodge") +
  geom_errorbar(aes(ymin = basal_area_m2_mean - basal_area_m2_sd,
                    ymax = basal_area_m2_mean + basal_area_m2_sd)) +
  facet_wrap(~cluster, scales = "free_y", labeller = label_both) +
  theme_bw() +
  theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1)) +
  geom_text(aes(label = species), vjust = 0.25, check_overlap = TRUE) +
  labs(title = "Species mean basal area per cluster (m²) in year 30")

# cohort count
ggplot(w_means_clim[w_means_clim$year == 30,],
       aes(x = species, y = cohort_count_ha_mean, fill = external_seed)) +
  geom_col(position = "dodge") +
  geom_errorbar(aes(ymin = cohort_count_ha_mean - cohort_count_ha_sd,
                    ymax = cohort_count_ha_mean + cohort_count_ha_sd)) +
  facet_wrap(~cluster, scales = "free_y", labeller = label_both) +
  theme_bw() +
  theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1)) +
  geom_text(aes(label = species), vjust = 0.25, check_overlap = TRUE) +
  labs(title = "Species mean cohort count per cluster (n/ha) in year 30")

3.1 Interesting plots

Biomass development and biomass state in year 30. Mean volume per scenario and cluster (y = mean vol, x = cluster).

# Development of biomass/volume over time
plot1 <- ggplot(w_means_clim, aes(x = year, y = volume_m3_mean, col = species)) +
  geom_line(aes(linetype = external_seed)) +
  geom_ribbon(aes(linetype = external_seed,
                  ymin = volume_m3_mean - volume_m3_sd,
                  ymax = volume_m3_mean + volume_m3_sd,
                  fill = species),
                  alpha = 0.05) +
  facet_wrap(~cluster, scales = "free") +
  theme_bw() +
  labs(title = "Development of mean volume(m³)/biomass per cluster")

# Volume/biomass state in year 30
plot2 <- ggplot(w_means_clim[w_means_clim$year == 30,],
       aes(x = species, y = volume_m3_mean, fill = external_seed)) +
  geom_col(position = "dodge") +
  geom_errorbar(aes(ymin = volume_m3_mean - volume_m3_sd,
                    ymax = volume_m3_mean + volume_m3_sd)) +
  facet_wrap(~cluster, scales = "free_y", labeller = label_both) +
  theme_bw() +
  theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1)) +
  geom_text(aes(label = species), vjust = 0.25, check_overlap = TRUE) +
  labs(title = "Species mean volume per cluster (m³)/Biomass in year 30")

# Mean volume per scenario and cluster in year 30
plot3 <- ggplot(w_means_clim[w_means_clim$year == 30,],
       aes(x = cluster, y = volume_m3_mean, fill = species)) +
  geom_col(col = "black") +
  geom_text(aes(label = species),
            position = position_stack(vjust = 0.5),
            size = 4,
            check_overlap = TRUE) +
  facet_wrap(~external_seed, scales = "free", labeller = label_both) +
  theme_bw() +
  labs(title = "Mean volume/biomass per cluster in year 30")

plot1
plot2
plot3

3.2 Save plots

Safe the 3 plots as .png file.

png("plots/plot1_year-vol.png", width = 1200, height = 1200)
plot1
dev.off()

png("plots/plot2_species-vol.png", width = 1200, height = 1200)
plot2
dev.off()

png("plots/plot3_cluster-vol.png", width = 1200, height = 1200)
plot3
dev.off()

4. Derive stand parameter

The development of the stand can also be predicted using 6 indicators, 4 of those should be calculated with iLand data (tree species, basal area, stem density, vertical class).

w_means_sapl_clim$external_seed <- FALSE
w_means_sapl_clim[w_means_sapl_clim$ext_seed == "seed",]$external_seed <- TRUE
w_means_sapl_clim <- dplyr::select(w_means_sapl_clim, !ext_seed)

w_means_sapD_clim$external_seed <- FALSE
w_means_sapD_clim[w_means_sapD_clim$ext_seed == "seed",]$external_seed <- TRUE
w_means_sapD_clim <- dplyr::select(w_means_sapD_clim, !ext_seed)

# Gather data for every cluster
w_indic <- inner_join(w_means_clim, w_means_sapl_clim,
                      by = join_by(external_seed, cluster, year, species))

w_indic <- w_indic %>%
  dplyr::rename(count_ha_4_mean = count_ha_mean.x, # tree count/ha (>4m height)
                count_ha_4_sd = count_ha_sd.x,
                co_cnt_ha_mean = cohort_count_ha_mean.x, # number of cohorts (<4m height)
                co_cnt_ha_sd = cohort_count_ha_sd.x, # number of cohorts (<4m height)
                cnt_ha_13_mean = count_ha_mean.y, # number of individuals/ha (>1.3m height)
                cnt_ha_13_sd = count_ha_sd.y # number of individuals/ha (>1.3m height)
              )
w_indic <- inner_join(w_indic, w_means_sapD_clim,
                      by = join_by(external_seed, cluster, year, species))
w_indic %>%
  dplyr::rename(n_represented_coho_mean = n_represented_mean,
                n_represented_coho_sd = n_represented_sd,
                coho_dbh_mean = dbh_mean,
                coho_dbh_sd = dbh_sd,
                coho_age_mean = age_mean,
                coho_age_sd = age_sd)

Description of columns:

column descriptions
Column name Description
external_seed Wheather external seed was considered in the simulation
cluster To which of the 12 clusters this line belongs
year simulated year (0 is initial state, 30 final state)
species tree species the data relates to
volume_m3_mean volume of species biomass (mean)
volume_m3_sd volume of species biomass (standard deviation)
dbh_avg_cm_mean average dbh of trees (>4m height) (mean)
dbh_avg_cm_sd average dbh of trees (>4m height) (standard deviation)
basal_area_m2_mean total basal area at breast height (>4m height) (mean)
basal_area_m2_sd total basal area at breast height (>4m height) (standard deviation)
count_ha_4_mean tree count (living, >4m height) per ha (mean)
count_ha_4_sd tree count (living, >4m height) per ha (standard deviation)
co_cnt_ha_mean number of cohorts in the regeneration layer (<4m) per ha (mean)
co_cnt_ha_sd number of cohorts in the regeneration layer (<4m) per ha (standard deviation)
cohort_basal_area_mean basal area of saplings (>1.3m and <4m) (mean)
cohort_basal_area_sd basal area of saplings (>1.3m and <4m) (standard deviation)
cnt_ha_13_mean number of represented individuals per ha (>1.3m height) (mean)
cnt_ha_13_sd number of represented individuals per ha (>1.3m height) (standard deviation)
count_small_ha_mean number of represented individuals per ha (<=1.3m height) (mean)
count_small_ha_sd number of represented individuals per ha (<=1.3m height) (standard deviation)
cohort_count_ha_mean.y number of cohorts per ha (same as co_cnt_ha_mean)
cohort_count_ha_sd.y number of cohorts per ha (same as co_cnt_ha_sd)
height_avg_m_mean arithmetic average height of the cohorts (m) (mean)
height_avg_m_sd arithmetic average height of the cohorts (m) (sd)
age_avg_mean arithmetric average age of the sapling cohorts (years) (mean)
age_avg_sd arithmetric average age of the sapling cohorts (years) (standard deviaion)
age_avg_sd arithmetric average age of the sapling cohorts (years) (standard deviaion)
n_represented_mean number of trees that are represented by the cohort (Reineke function) (mean)
n_represented_sd number of trees that are represented by the cohort (Reineke function) (sd)
dbh_mean diameter of the cohort in cm (mean)
dbh_sd diameter of the cohort in cm (sd)
height_mean height of the cohort in m (mean)
height_sd height of the cohort in m (sd)
age_mean age of the cohort (mean)
age_sd age of the cohort (sd)
LS0tDQp0aXRsZTogIlIgTm90ZWJvb2sgLSBBbmFseXNlIGlMYW5kIG91dHB1dCINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCkFmdGVyIGhhdmluZyBydW4gdGhlIGlMYW5kIHNpbXVsYXRpb25zIGZvciB0aGUgMTIgY2x1c3RlcnMgdGhlIHJlc3VsdGluZyAuc3FsaXRlIGZpbGVzIG5lZWQgdG8gYmUgZXZhbHVhdGVkLiBHb2FsIGlzIHRvIHNlZSBob3cgdGhlIHRyZWUgZGV2ZWxvcG1lbnQgb2YgZWFjaCBjbHVzdGVyIGRldmVsb3BzLg0KDQojIDEuIExvYWQgZGF0YSBmcm9tIC5zcWxpdGUgZmlsZXMNCg0KVG8gYmUgYWJsZSB0byBsb2FkIHRoZSBkYXRhLCBmaXN0IGEgY29ubmVjdGlvbiB0byB0aGUgLnNxbGl0ZSBmaWxlIG5lZWRzIHRvIGJlIGVzdGFibGlzaGVkLiBGb3IgdGhpcyB0aGUgbGlicmFyeSAiUlNRTGl0ZSIgaXMgbmVlZGVkLg0KDQpgYGB7ciBsaWJyYXJ5c30NCiNpbnN0YWxsLnBhY2thZ2VzKCJSU1FMaXRlIikNCmxpYnJhcnkoUlNRTGl0ZSkNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHRpZHlyKQ0KbGlicmFyeShnZ3B1YnIpDQpsaWJyYXJ5KHJzdGF0aXgpDQpsaWJyYXJ5KGdncGxvdDIpDQpgYGANCg0KV2l0aCB0aGUgbmVjZXNzYXJ5IGxpYnJhcmllcyBsb2FkZWQsIHRoZSBkYXRhIGNhbiBiZSBhc3Nlc3NlZC4gRmlyc3QgYSBjb25uZWN0aW9uIHRvIHRoZSBkYXRhYmFzZSBpcyBlc3RhYmxpc2hlZCwgYWZ0ZXJ3YXJkcyB0aGUgdGFibGVzIGluc2lkZSBjYW4gYmUgcmVhZC4NCg0KYGBge3IgbG9hZF9kYXRhfQ0Kc2V0d2QoInJhd0RhdGEvaUxhbmQvaUxhbmRfb3V0cHV0IikNCmdldHdkKCkNCg0KIyBHZXQgb3V0cHV0IGRpcnMgYW5kIHJ1biB0aHJvdWdoIHRoZW0NCmxEaXJzIDwtIGxpc3QuZGlycyhyZWN1cnNpdmUgPSBGQUxTRSkNCmxEaXJzIDwtIGdyZXAoIl9uZXciLCBsRGlycywgdmFsdWUgPSBUUlVFKQ0KREJUYWJsZXMgPC0gbGlzdCgpDQpEQlRfc2FwIDwtIGxpc3QoKQ0KREJUX3NhcGQgPC0gbGlzdCgpDQppRmlsZXMgPC0gMQ0KaURpcnMgPC0gMQ0KYkV4dFNlZWQgPC0gRkFMU0UNCg0KIyBPdXRlciBsb29wIHJ1bnMgdGhyb3VnaCBhbGwgZGlyZWN0b3JpZXMgYXQgdGhhdCBwYXRoIGFuZCBsb2FkcyAuc3FsaXRlIGZpbGVzDQojIGludG8gYSBsaXN0LiBBbHNvIGEgY291bnRlciAoaURpcnMpIGNvdW50cyB0aGUgbnVtYmVyIG9mIGRpcmVjdG9yaWVzIGFjY2Vzc2VkDQpmb3IgKGxfZGlyIGluIGxEaXJzKSB7DQogIGxGaWxlcyA8LSBsaXN0LmZpbGVzKHBhdGggPSBsX2RpciwgcGF0dGVybiA9ICIuc3FsaXRlIiwgZnVsbC5uYW1lcyA9IFRSVUUpDQogIA0KICAjIElubmVyIGxvb3AgcnVucyB0aHJvdWdoIGFsbCBmaWxlcyAoLnNxbGl0ZSkgaW4gYSBkaXJlY3RvcnkgYW5kIGxvYWRzIHRoZQ0KICAjIHN0YW5kIHRhYmxlIGludG8gYSB2YXJpYWJsZS4NCiAgIyBGaWxlIGZvciBjbHVzdGVyIDNfMiBpcyBza2lwcGVkLCBkdWUgdG8gdGhpcyBzdGFuZCBiZWluZyBlbXB0eSBpbiBydW4gDQogICMgd2l0aG91dCBleHRlcm5hbCBzZWVlZHMNCiAgZm9yIChsRmlsZSBpbiBsRmlsZXMpIHsNCiAgICBsX2RiIDwtIGRiQ29ubmVjdChSU1FMaXRlOjpTUUxpdGUoKSwgbEZpbGUpDQogICAgbF9kZlN0bmQgPC0gZGJSZWFkVGFibGUobF9kYiwgInN0YW5kIikNCiAgICBsX2RmU2FwbCA8LSBkYlJlYWRUYWJsZShsX2RiLCAic2FwbGluZyIpDQogICAgbF9kZlNhcEQgPC0gZGJSZWFkVGFibGUobF9kYiwgInNhcGxpbmdkZXRhaWwiKQ0KICAgIGRiRGlzY29ubmVjdChsX2RiKQ0KICAgIA0KICAgIGJFeHRTZWVkIDwtICFncmVwbCgibm9zZWVkIiwgbEZpbGUpDQoNCiAgICBpZiAobnJvdyhsX2RmU3RuZCkgPT0gMCkgew0KICAgICAgbmV4dA0KICAgIH0NCiAgICANCiAgICBsX2RmU3RuZCRmaWxlbm0gPC0gdG9vbHM6OmZpbGVfcGF0aF9zYW5zX2V4dChiYXNlbmFtZShsRmlsZSkpDQogICAgbF9kZlN0bmQgPC0gbF9kZlN0bmQgJT4lDQogICAgICBkcGx5cjo6c2VsZWN0KGZpbGVubSwNCiAgICAgICAgICAgICAgICAgICAgeWVhciwNCiAgICAgICAgICAgICAgICAgICAgc3BlY2llcywNCiAgICAgICAgICAgICAgICAgICAgdm9sdW1lX20zLA0KICAgICAgICAgICAgICAgICAgICBkYmhfYXZnX2NtLA0KICAgICAgICAgICAgICAgICAgICBiYXNhbF9hcmVhX20yLA0KICAgICAgICAgICAgICAgICAgICBjb3VudF9oYSwNCiAgICAgICAgICAgICAgICAgICAgY29ob3J0X2NvdW50X2hhLA0KICAgICAgICAgICAgICAgICAgICBjb2hvcnRfYmFzYWxfYXJlYSkNCiAgICBsX2RmU3RuZCRydW5fbnIgPC0gaURpcnMNCiAgICBsX2RmU3RuZCRleHRlcm5hbF9zZWVkIDwtIGJFeHRTZWVkDQogICAgDQogICAgbF9kZlNhcGwkZmlsZW5tIDwtIHRvb2xzOjpmaWxlX3BhdGhfc2Fuc19leHQoYmFzZW5hbWUobEZpbGUpKQ0KICAgIGxfZGZTYXBEJGZpbGVubSA8LSB0b29sczo6ZmlsZV9wYXRoX3NhbnNfZXh0KGJhc2VuYW1lKGxGaWxlKSkNCiAgICANCiAgICBEQlRhYmxlc1tbaUZpbGVzXV0gPC0gbF9kZlN0bmQNCiAgICBEQlRfc2FwW1tpRmlsZXNdXSA8LSBsX2RmU2FwbA0KICAgIERCVF9zYXBkW1tpRmlsZXNdXSA8LSBsX2RmU2FwRA0KICAgIA0KICAgIGlGaWxlcyA8LSBpRmlsZXMgKyAxDQogICAgI2RiRGlzY29ubmVjdChsX2RiKQ0KICB9DQogIGlEaXJzIDwtIGlEaXJzICsgMQ0KfQ0KIyBCcmluZyBhbGwgdGhlIGRhdGEgdG9nZXRoZXIgaW4gYmlnX2RhdGEgdmFyaWFibGUNCk9HX2JpZ19kYXRhIDwtIGJpbmRfcm93cyhEQlRhYmxlcykNCk9HX3NhcGwgPC0gYmluZF9yb3dzKERCVF9zYXApDQpPR19zYXBkIDwtIGJpbmRfcm93cyhEQlRfc2FwZCkNCg0KIyBDbGVhbnVwIG9mIHNvbWUgdmFyaWFibGVzDQpybShEQlRhYmxlcywgbF9kYiwgbF9kZlN0bmQsIGlEaXJzLCBpRmlsZXMsIGxfZGlyLCBsRGlycywgbEZpbGUsIGxGaWxlcywgDQogICBiRXh0U2VlZCwgREJUX3NhcGQsIERCVF9zYXAsIGxfZGZTYXBsLCBsX2RmU2FwRCkNCmBgYA0KDQojIDIuIERhdGEgaGFuZGxpbmcNCg0KRm9yIGZ1cnRoZXIgcHJvY2Vzc2luZyBpdCBtaWdodCBiZSB1c2VmdWxsIHRvIHNwbGl0IHRoZSBmaWxlbmFtZSBpbnRvIGEgZmV3IHBpZWNlczoNCg0KLSAgIENsaW1hdGUgbW9kZWwNCg0KLSAgIENsdXN0ZXINCg0KLSAgIFdpdGggb3Igd2l0aG91dCBleHRlcm5hbCBzZWVkDQoNCmBgYHtyIGRhdGFfaGFuZGxpbmd9DQojIDEuIHN0YW5kIHRhYmxlDQpiaWdfZGF0YSA8LSBPR19iaWdfZGF0YSB8Pg0KICBzZXBhcmF0ZV93aWRlcl9kZWxpbShmaWxlbm0sIGRlbGltID0gIl8iLCBuYW1lcyA9IGMoImNsaW1fbW9kZWwiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImNsaW1fc2NlbmFyaW8iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImVudl9jbHN0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzdG5kX2Nsc3QiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImV4dF9zZWVkIikpDQpiaWdfZGF0YSA8LSBiaWdfZGF0YSAlPiUgZHBseXI6OnNlbGVjdCghZXh0X3NlZWQpDQpiaWdfZGF0YSRjbHVzdGVyIDwtIHBhc3RlKGJpZ19kYXRhJGVudl9jbHN0LCBiaWdfZGF0YSRzdG5kX2Nsc3QsIHNlcCA9ICJfIikNCmJpZ19kYXRhIDwtIGJpZ19kYXRhICU+JQ0KICBkcGx5cjo6c2VsZWN0KCFjKGVudl9jbHN0LCBzdG5kX2Nsc3QpKQ0KYmlnX2RhdGEkY2xpbV9zY2VuYXJpbyA8LSBhcy5mYWN0b3IoYmlnX2RhdGEkY2xpbV9zY2VuYXJpbykNCmJpZ19kYXRhJGNsaW1fbW9kZWwgPC0gYXMuZmFjdG9yKGJpZ19kYXRhJGNsaW1fbW9kZWwpDQpiaWdfZGF0YSRjbHVzdGVyIDwtIGFzLmZhY3RvcihiaWdfZGF0YSRjbHVzdGVyKQ0KYmlnX2RhdGEkc3BlY2llcyA8LSBhcy5mYWN0b3IoYmlnX2RhdGEkc3BlY2llcykNCg0Kc3RyQ29scyA8LSBjKCJ2b2x1bWVfbTMiLCAiZGJoX2F2Z19jbSIsICJiYXNhbF9hcmVhX20yIiwNCiAgICAgICAgICAgICAiY291bnRfaGEiLCAiY29ob3J0X2NvdW50X2hhIiwgImNvaG9ydF9iYXNhbF9hcmVhIikNCg0Kd19tZWFucyA8LSBiaWdfZGF0YSAlPiUNCiAgZHBseXI6Omdyb3VwX2J5KGNsaW1fc2NlbmFyaW8sIGV4dGVybmFsX3NlZWQsIGNsdXN0ZXIsIHllYXIsIHNwZWNpZXMpICU+JQ0KICBkcGx5cjo6c3VtbWFyaXNlKGFjcm9zcyhzdHJDb2xzLCBsaXN0KG1lYW4gPSBtZWFuLCBzZCA9IHNkKSksLmdyb3VwcyA9ICJkcm9wIikNCg0KIyAyLiBzYXBsaW5nIHRhYmxlDQpiaWdfc2FwbCA8LSBPR19zYXBsIHw+DQogIHNlcGFyYXRlX3dpZGVyX2RlbGltKGZpbGVubSwgZGVsaW0gPSAiXyIsIG5hbWVzID0gYygiY2xpbV9tb2RlbCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiY2xpbV9zY2VuYXJpbyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZW52X2Nsc3QiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInN0bmRfY2xzdCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZXh0X3NlZWQiKSkNCmJpZ19zYXBsJGNsdXN0ZXIgPC0gcGFzdGUoYmlnX3NhcGwkZW52X2Nsc3QsIGJpZ19zYXBsJHN0bmRfY2xzdCwgc2VwID0gIl8iKQ0KYmlnX3NhcGwkY2xpbV9tb2RlbCA8LSBhcy5mYWN0b3IoYmlnX3NhcGwkY2xpbV9tb2RlbCkNCmJpZ19zYXBsJGNsaW1fc2NlbmFyaW8gPC0gYXMuZmFjdG9yKGJpZ19zYXBsJGNsaW1fc2NlbmFyaW8pDQpiaWdfc2FwbCRjbHVzdGVyIDwtIGFzLmZhY3RvcihiaWdfc2FwbCRjbHVzdGVyKQ0KYmlnX3NhcGwkc3BlY2llcyA8LSBhcy5mYWN0b3IoYmlnX3NhcGwkc3BlY2llcykNCmJpZ19zYXBsJGV4dF9zZWVkIDwtIGFzLmZhY3RvcihiaWdfc2FwbCRleHRfc2VlZCkNCmJpZ19zYXBsIDwtIGJpZ19zYXBsICU+JSBkcGx5cjo6c2VsZWN0KCFjKGVudl9jbHN0LCBzdG5kX2Nsc3QsIHJ1LCByaWQpKQ0KDQpzdHJDb2xzIDwtIGMoImNvdW50X2hhIiwgImNvdW50X3NtYWxsX2hhIiwgImNvaG9ydF9jb3VudF9oYSIsICJoZWlnaHRfYXZnX20iLA0KICAgICAgICAgICAgICJhZ2VfYXZnIiwgIkxBSSIpDQoNCndfbWVhbnNfc2FwbCA8LSBiaWdfc2FwbCAlPiUNCiAgZHBseXI6Omdyb3VwX2J5KGNsaW1fc2NlbmFyaW8sIGV4dF9zZWVkLCBjbHVzdGVyLCB5ZWFyLCBzcGVjaWVzKSAlPiUNCiAgZHBseXI6OnN1bW1hcmlzZShhY3Jvc3Moc3RyQ29scywgbGlzdChtZWFuID0gbWVhbiwgc2QgPSBzZCkpLC5ncm91cHMgPSAiZHJvcCIpDQoNCiMgMy4gc2FwbGluZ2RldGFpbHMgdGFibGUNCmJpZ19zYXBEIDwtIE9HX3NhcGQgfD4NCiAgc2VwYXJhdGVfd2lkZXJfZGVsaW0oZmlsZW5tLCBkZWxpbSA9ICJfIiwgbmFtZXMgPSBjKCJjbGltX21vZGVsIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJjbGltX3NjZW5hcmlvIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJlbnZfY2xzdCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3RuZF9jbHN0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJleHRfc2VlZCIpKQ0KYmlnX3NhcEQkY2x1c3RlciA8LSBwYXN0ZShiaWdfc2FwRCRlbnZfY2xzdCwgYmlnX3NhcEQkc3RuZF9jbHN0LCBzZXAgPSAiXyIpDQpiaWdfc2FwRCRjbGltX21vZGVsIDwtIGFzLmZhY3RvcihiaWdfc2FwRCRjbGltX21vZGVsKQ0KYmlnX3NhcEQkY2xpbV9zY2VuYXJpbyA8LSBhcy5mYWN0b3IoYmlnX3NhcEQkY2xpbV9zY2VuYXJpbykNCmJpZ19zYXBEJGNsdXN0ZXIgPC0gYXMuZmFjdG9yKGJpZ19zYXBEJGNsdXN0ZXIpDQpiaWdfc2FwRCRzcGVjaWVzIDwtIGFzLmZhY3RvcihiaWdfc2FwRCRzcGVjaWVzKQ0KYmlnX3NhcEQkZXh0X3NlZWQgPC0gYXMuZmFjdG9yKGJpZ19zYXBEJGV4dF9zZWVkKQ0KYmlnX3NhcEQgPC0gYmlnX3NhcEQgJT4lIGRwbHlyOjpzZWxlY3QoIWMoZW52X2Nsc3QsIHN0bmRfY2xzdCwgcnUsIHJpZCkpDQoNCnN0ckNvbHMgPC0gYygiYWdlIiwgImhlaWdodCIsICJkYmgiLCAibl9yZXByZXNlbnRlZCIpDQoNCndfbWVhbnNfc2FwRCA8LSBiaWdfc2FwRCAlPiUNCiAgZHBseXI6Omdyb3VwX2J5KGNsaW1fc2NlbmFyaW8sIGV4dF9zZWVkLCBjbHVzdGVyLCB5ZWFyLCBzcGVjaWVzKSAlPiUNCiAgZHBseXI6OnN1bW1hcmlzZShhY3Jvc3Moc3RyQ29scywgbGlzdChtZWFuID0gbWVhbiwgc2QgPSBzZCkpLC5ncm91cHMgPSAiZHJvcCIpDQoNCiMgY2xlYW4gZW52aXJvbm1lbnQNCnJtKHN0ckNvbHMpDQpgYGANCg0KIyMgMi4xIEVmZmVjdCBzdHJlbmd0aA0KDQpFZmZlY3Qgc3RyZW5ndGggb2YgYWxsIHZhcmlhYmxlcyB0aGF0IGluZmx1ZW5jZSB0aGUgbnVtYmVyIG9mIHRyZWVzIGlzIGNhbGN1bGF0ZWQgdG8gaWRlbnRpZnkgd2hpY2ggY2FuIGJlIGluY2x1ZGVkIGluIHRoZSBtZWFuIGNhbGN1bGF0aW9uLg0KDQpgYGB7ciBlZmZlY3Rfc3RyZW5ndGh9DQpnZ3Bsb3Qod19tZWFucywgYWVzKHggPSBjb3VudF9oYV9tZWFuKSkgKyBnZW9tX2hpc3RvZ3JhbSgpDQpnZ3Bsb3Qod19tZWFucywgYWVzKHggPSBjb3VudF9oYV9tZWFuKSkgKyBnZW9tX2RlbnNpdHkoKQ0KDQojIHRlc3QgZm9yIHZhcmlhbnpob21vZ2VuaW9jaXR5IChpZiBwIDwgMC4wNSAtPiBubyBob21vZ2VuZWl0eSBvZiB2YXJpYW5jZSkNCndfbWVhbnMgJT4lIGxldmVuZV90ZXN0KGNvdW50X2hhX21lYW4gfiBjbGltX3NjZW5hcmlvKSAgI2hvbW9nZW5laXR5IG9mIHZhcmlhbmNlDQp3X21lYW5zICU+JSBsZXZlbmVfdGVzdCh2b2x1bWVfbTNfbWVhbiB+IGNsaW1fc2NlbmFyaW8pICAjaG9tb2dlbmVpdHkgb2YgdmFyaWFuY2UNCndfbWVhbnMgJT4lIGxldmVuZV90ZXN0KGNvdW50X2hhX21lYW4gfiBleHRlcm5hbF9zZWVkKSAgI25vIGhvbW9nZW5laXR5IG9mIHZhcg0Kd19tZWFucyAlPiUgbGV2ZW5lX3Rlc3QoY291bnRfaGFfbWVhbiB+IGNsdXN0ZXIpICAgICAgICAjbm8gaG9tb2dlbmVpdHkgb2YgdmFyDQp3X21lYW5zICU+JSBsZXZlbmVfdGVzdChjb3VudF9oYV9tZWFuIH4gc3BlY2llcykgICAgICAgICNubyBob21vZ2VuZWl0eSBvZiB2YXINCg0KIyB0ZXN0IGZvciBlZmZlY3Qgc3RyZW5ndGggLSB2YXIuZXF1YWwgPSBGQUxTRSAtPiBubyBob21vZ2VuZWl0eSBvZiB2YXJpYW5jZQ0Kd19tZWFucyAlPiUgY29oZW5zX2QoY291bnRfaGFfbWVhbiB+IGNsaW1fc2NlbmFyaW8sIHZhci5lcXVhbCA9IFRSVUUpICNuZWdsaWdpYmxlIGVmZmVjdA0Kd19tZWFucyAlPiUgY29oZW5zX2Qodm9sdW1lX20zX21lYW4gfiBjbGltX3NjZW5hcmlvLCB2YXIuZXF1YWwgPSBUUlVFKSAjbmVnbGlnaWJsZSBlZmZlY3QNCndfbWVhbnMgJT4lIGNvaGVuc19kKGNvdW50X2hhX21lYW4gfiBleHRlcm5hbF9zZWVkLCB2YXIuZXF1YWwgPSBGQUxTRSkgI3NtYWxsIGVmZmVjdA0Kd19tZWFucyAlPiUgY29oZW5zX2QoY291bnRfaGFfbWVhbiB+IGNsdXN0ZXIsIHZhci5lcXVhbCA9IEZBTFNFKSAjbm8gdG8gbGFyZ2UgZWZmZWN0DQp3X21lYW5zICU+JSBjb2hlbnNfZChjb3VudF9oYV9tZWFuIH4gc3BlY2llcywgdmFyLmVxdWFsID0gRkFMU0UpICNubyB0byBsYXJnZSBlZmZlY3QNCg0KZ2dwbG90KHdfbWVhbnNbd19tZWFucyR5ZWFyID09IDMwICYgd19tZWFucyRleHRlcm5hbF9zZWVkID09IEZBTFNFLF0sIA0KICAgICAgIGFlcyh4ID0gc3BlY2llcyx5ID0gY291bnRfaGFfbWVhbiwgZmlsbCA9IHNwZWNpZXMpKSArDQogIGdlb21fY29sKHBvc2l0aW9uID0gImRvZGdlIikgKw0KICBnZW9tX2Vycm9yYmFyKGFlcyh5bWluID0gY291bnRfaGFfbWVhbiAtIGNvdW50X2hhX3NkLA0KICAgICAgICAgICAgICAgICAgICB5bWF4ID0gY291bnRfaGFfbWVhbiArIGNvdW50X2hhX3NkKSkgKw0KICBmYWNldF9ncmlkKGNsdXN0ZXIgfiBjbGltX3NjZW5hcmlvLCBzY2FsZXMgPSAiZnJlZSIpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAwLjUsIGhqdXN0ID0gMSkpICsNCiAgbGFicyh0aXRsZSA9ICJDb3VudCBwZXIgaGEsIGluIFllYXIgMzAgKG5vIGV4dGVybmFsIHNlZWQpIikNCmBgYA0KDQpDbGltYXRlIHNjZW5hcmlvcyBkbyBub3Qgc2VlbSB0byBoYXZlIGEgaHVnZSBpbmZsdWVuY2Ugb24gdGhlIGRhdGFzZXQgYW5kIGNhbiB0aGVyZWZvcmUgY2FsY3VsYXRlZCBpbnRvIHRoZSB1bmNlcnRhaW50eS4NCg0KIyMgMi4yIE1lYW4gb3ZlciBhbGwgY2xpbV9zY2VuYXJpb3MNCg0KQ2FsY3VsYXRlIHRoZSBtZWFuIHZhbHVlcyBvdmVyIGFsbCA0IGNsaW1hdGUgc2NlbmFyaW9zLg0KDQpgYGB7ciBtZWFuX2NsaW19DQojIHN0YW5kDQpzdHJDb2xzIDwtIGMoInZvbHVtZV9tMyIsICJkYmhfYXZnX2NtIiwgImJhc2FsX2FyZWFfbTIiLA0KICAgICAgICAgICAgICJjb3VudF9oYSIsICJjb2hvcnRfY291bnRfaGEiLCAiY29ob3J0X2Jhc2FsX2FyZWEiKQ0KDQp3X21lYW5zX2NsaW0gPC0gYmlnX2RhdGEgJT4lDQogIGRwbHlyOjpncm91cF9ieShleHRlcm5hbF9zZWVkLCBjbHVzdGVyLCB5ZWFyLCBzcGVjaWVzKSAlPiUNCiAgZHBseXI6OnN1bW1hcmlzZShhY3Jvc3Moc3RyQ29scywgbGlzdChtZWFuID0gbWVhbiwgc2QgPSBzZCkpLA0KICAgICAgICAgICAgICAgICAgIC5ncm91cHMgPSAiZHJvcCIpDQoNCiMgc2FwbGluZ3MNCnN0ckNvbHMgPC0gYygiY291bnRfaGEiLCAiY291bnRfc21hbGxfaGEiLCAiY29ob3J0X2NvdW50X2hhIiwNCiAgICAgICAgICAgICAiaGVpZ2h0X2F2Z19tIiwgImFnZV9hdmciKQ0KDQp3X21lYW5zX3NhcGxfY2xpbSA8LSBiaWdfc2FwbCAlPiUNCiAgZHBseXI6Omdyb3VwX2J5KGV4dF9zZWVkLCBjbHVzdGVyLCB5ZWFyLCBzcGVjaWVzKSAlPiUNCiAgZHBseXI6OnN1bW1hcmlzZShhY3Jvc3Moc3RyQ29scywgbGlzdChtZWFuID0gbWVhbiwgc2QgPSBzZCkpLA0KICAgICAgICAgICAgICAgICAgIC5ncm91cHMgPSAiZHJvcCIpDQoNCiMgc2FwbGluZyBkZXRhaWxzDQpzdHJDb2xzIDwtIGMoIm5fcmVwcmVzZW50ZWQiLCAiZGJoIiwgImhlaWdodCIsICJhZ2UiKQ0KDQp3X21lYW5zX3NhcERfY2xpbSA8LSBiaWdfc2FwRCAlPiUNCiAgZHBseXI6Omdyb3VwX2J5KGV4dF9zZWVkLCBjbHVzdGVyLCB5ZWFyLCBzcGVjaWVzKSAlPiUNCiAgZHBseXI6OnN1bW1hcmlzZShhY3Jvc3Moc3RyQ29scywgbGlzdChtZWFuID0gbWVhbiwgc2QgPSBzZCkpLA0KICAgICAgICAgICAgICAgICAgIC5ncm91cHMgPSAiZHJvcCIpDQoNCnJtKHN0ckNvbHMpDQpgYGANCg0KIyAzLiBQbG90cw0KDQoxLiAgRGV2ZWxvcG1lbnQgdm9sdW1lLCBkYmgsIGJhc2FsIGFyZWEsIGNvdW50IGFuZCBjb2hvcnQgY291bnQgb3ZlciB0aGUgc2ltdWxhdGlvbiBleHRlbmQuDQoNCmBgYHtyIGRldmVsb3BtZW50X3Bsb3RzfQ0KIyB2b2x1bWUvYmlvbWFzcw0KZ2dwbG90KHdfbWVhbnNfY2xpbSwgYWVzKHggPSB5ZWFyLCB5ID0gdm9sdW1lX20zX21lYW4sIGNvbCA9IHNwZWNpZXMpKSArDQogIGdlb21fbGluZShhZXMobGluZXR5cGUgPSBleHRlcm5hbF9zZWVkKSkgKw0KICBnZW9tX3JpYmJvbihhZXMobGluZXR5cGUgPSBleHRlcm5hbF9zZWVkLA0KICAgICAgICAgICAgICAgICAgeW1pbiA9IHZvbHVtZV9tM19tZWFuIC0gdm9sdW1lX20zX3NkLA0KICAgICAgICAgICAgICAgICAgeW1heCA9IHZvbHVtZV9tM19tZWFuICsgdm9sdW1lX20zX3NkLA0KICAgICAgICAgICAgICAgICAgZmlsbCA9IHNwZWNpZXMpLA0KICAgICAgICAgICAgICAgICAgYWxwaGEgPSAwLjA1KSArDQogIGZhY2V0X3dyYXAofmNsdXN0ZXIsIHNjYWxlcyA9ICJmcmVlIikgKw0KICB0aGVtZV9idygpICsNCiAgbGFicyh0aXRsZSA9ICJEZXZlbG9wbWVudCBvZiBtZWFuIHZvbHVtZShtwrMpL2Jpb21hc3MgcGVyIGNsdXN0ZXIiKQ0KDQojIGRiaA0KZ2dwbG90KHdfbWVhbnNfY2xpbSwgYWVzKHggPSB5ZWFyLCB5ID0gZGJoX2F2Z19jbV9tZWFuLCBjb2wgPSBzcGVjaWVzKSkgKw0KICBnZW9tX2xpbmUoYWVzKGxpbmV0eXBlID0gZXh0ZXJuYWxfc2VlZCkpICsNCiAgZ2VvbV9yaWJib24oYWVzKGxpbmV0eXBlID0gZXh0ZXJuYWxfc2VlZCwNCiAgICAgICAgICAgICAgICAgIHltaW4gPSBkYmhfYXZnX2NtX21lYW4gLSBkYmhfYXZnX2NtX3NkLA0KICAgICAgICAgICAgICAgICAgeW1heCA9IGRiaF9hdmdfY21fbWVhbiArIGRiaF9hdmdfY21fc2QsDQogICAgICAgICAgICAgICAgICBmaWxsID0gc3BlY2llcyksDQogICAgICAgICAgICAgICAgICBhbHBoYSA9IDAuMDUpICsNCiAgZmFjZXRfd3JhcCh+Y2x1c3Rlciwgc2NhbGVzID0gImZyZWUiKSArDQogIHRoZW1lX2J3KCkgKw0KICBsYWJzKHRpdGxlID0gIkRldmVsb3BtZW50IG9mIG1lYW4gZGJoKGNtKSBwZXIgY2x1c3RlciIpDQoNCiMgYmFzYWwgYXJlYQ0KZ2dwbG90KHdfbWVhbnNfY2xpbSwgYWVzKHggPSB5ZWFyLCB5ID0gYmFzYWxfYXJlYV9tMl9tZWFuLCBjb2wgPSBzcGVjaWVzKSkgKw0KICBnZW9tX2xpbmUoYWVzKGxpbmV0eXBlID0gZXh0ZXJuYWxfc2VlZCkpICsNCiAgZ2VvbV9yaWJib24oYWVzKGxpbmV0eXBlID0gZXh0ZXJuYWxfc2VlZCwNCiAgICAgICAgICAgICAgICAgIHltaW4gPSBiYXNhbF9hcmVhX20yX21lYW4gLSBiYXNhbF9hcmVhX20yX3NkLA0KICAgICAgICAgICAgICAgICAgeW1heCA9IGJhc2FsX2FyZWFfbTJfbWVhbiArIGJhc2FsX2FyZWFfbTJfc2QsDQogICAgICAgICAgICAgICAgICBmaWxsID0gc3BlY2llcyksDQogICAgICAgICAgICAgICAgICBhbHBoYSA9IDAuMDUpICsNCiAgZmFjZXRfd3JhcCh+Y2x1c3Rlciwgc2NhbGVzID0gImZyZWUiKSArDQogIHRoZW1lX2J3KCkgKw0KICBsYWJzKHRpdGxlID0gIkRldmVsb3BtZW50IG9mIG1lYW4gYmFzYWwgYXJlYSAobcKyKSBwZXIgY2x1c3RlciIpDQoNCiMgc3RlbSBjb3VudA0KZ2dwbG90KHdfbWVhbnNfY2xpbSwgYWVzKHggPSB5ZWFyLCB5ID0gY291bnRfaGFfbWVhbiwgY29sID0gc3BlY2llcykpICsNCiAgZ2VvbV9saW5lKGFlcyhsaW5ldHlwZSA9IGV4dGVybmFsX3NlZWQpKSArDQogIGdlb21fcmliYm9uKGFlcyhsaW5ldHlwZSA9IGV4dGVybmFsX3NlZWQsDQogICAgICAgICAgICAgICAgICB5bWluID0gY291bnRfaGFfbWVhbiAtIGNvdW50X2hhX3NkLA0KICAgICAgICAgICAgICAgICAgeW1heCA9IGNvdW50X2hhX21lYW4gKyBjb3VudF9oYV9zZCwNCiAgICAgICAgICAgICAgICAgIGZpbGwgPSBzcGVjaWVzKSwNCiAgICAgICAgICAgICAgICAgIGFscGhhID0gMC4wNSkgKw0KICBmYWNldF93cmFwKH5jbHVzdGVyLCBzY2FsZXMgPSAiZnJlZSIpICsNCiAgdGhlbWVfYncoKSArDQogIGxhYnModGl0bGUgPSAiRGV2ZWxvcG1lbnQgb2YgbWVhbiBzdGVtIGNvdW50IChuL2hhKSBwZXIgY2x1c3RlciIpDQoNCiMgY29ob3J0IGNvdW50DQpnZ3Bsb3Qod19tZWFuc19jbGltW3dfbWVhbnNfY2xpbSRleHRlcm5hbF9zZWVkID09IEZBTFNFLF0sDQogICAgICAgYWVzKHggPSB5ZWFyLCB5ID0gY29ob3J0X2NvdW50X2hhX21lYW4sIGNvbCA9IHNwZWNpZXMpKSArDQogIGdlb21fbGluZShhZXMobGluZXR5cGUgPSBleHRlcm5hbF9zZWVkKSkgKw0KICBnZW9tX3JpYmJvbihhZXMobGluZXR5cGUgPSBleHRlcm5hbF9zZWVkLA0KICAgICAgICAgICAgICAgICAgeW1pbiA9IGNvaG9ydF9jb3VudF9oYV9tZWFuIC0gY29ob3J0X2NvdW50X2hhX3NkLA0KICAgICAgICAgICAgICAgICAgeW1heCA9IGNvaG9ydF9jb3VudF9oYV9tZWFuICsgY29ob3J0X2NvdW50X2hhX3NkLA0KICAgICAgICAgICAgICAgICAgZmlsbCA9IHNwZWNpZXMpLA0KICAgICAgICAgICAgICAgICAgYWxwaGEgPSAwLjA1KSArDQogIGZhY2V0X3dyYXAofmNsdXN0ZXIsIHNjYWxlcyA9ICJmcmVlIikgKw0KICB0aGVtZV9idygpICsNCiAgbGFicyh0aXRsZSA9ICJEZXZlbG9wbWVudCBvZiBtZWFuIGNvaG9ydCBjb3VudCAobi9oYSkgcGVyIGNsdXN0ZXIgKG5vIHNlZWQpIikNCg0KZ2dwbG90KHdfbWVhbnNfY2xpbVt3X21lYW5zX2NsaW0kZXh0ZXJuYWxfc2VlZCA9PSBUUlVFLF0sDQogICAgICAgYWVzKHggPSB5ZWFyLCB5ID0gY29ob3J0X2NvdW50X2hhX21lYW4sIGNvbCA9IHNwZWNpZXMpKSArDQogIGdlb21fbGluZShhZXMobGluZXR5cGUgPSBleHRlcm5hbF9zZWVkKSkgKw0KICBnZW9tX3JpYmJvbihhZXMobGluZXR5cGUgPSBleHRlcm5hbF9zZWVkLA0KICAgICAgICAgICAgICAgICAgeW1pbiA9IGNvaG9ydF9jb3VudF9oYV9tZWFuIC0gY29ob3J0X2NvdW50X2hhX3NkLA0KICAgICAgICAgICAgICAgICAgeW1heCA9IGNvaG9ydF9jb3VudF9oYV9tZWFuICsgY29ob3J0X2NvdW50X2hhX3NkLA0KICAgICAgICAgICAgICAgICAgZmlsbCA9IHNwZWNpZXMpLA0KICAgICAgICAgICAgICAgICAgYWxwaGEgPSAwLjA1KSArDQogIGZhY2V0X3dyYXAofmNsdXN0ZXIsIHNjYWxlcyA9ICJmcmVlIikgKw0KICB0aGVtZV9idygpICsNCiAgbGFicyh0aXRsZSA9ICJEZXZlbG9wbWVudCBvZiBtZWFuIGNvaG9ydCBjb3VudCAobi9oYSkgcGVyIGNsdXN0ZXIiKQ0KYGBgDQoNCjIuICBEb21pbmFudCBzcGVjaWVzIG9uIGVhY2ggY2x1c3RlciBpbiBZZWFyIDMwDQoNCmBgYHtyIGRvbWluYW50X3NwZWNpZXNfcGxvdHN9DQojIHN0ZW0gY291bnQNCmdncGxvdCh3X21lYW5zX2NsaW1bd19tZWFuc19jbGltJHllYXIgPT0gMzAsXSwNCiAgICAgICBhZXMoeCA9IHNwZWNpZXMsIHkgPSBjb3VudF9oYV9tZWFuLCBmaWxsID0gZXh0ZXJuYWxfc2VlZCkpICsNCiAgZ2VvbV9jb2wocG9zaXRpb24gPSAiZG9kZ2UiKSArDQogIGdlb21fZXJyb3JiYXIoYWVzKHltaW4gPSBjb3VudF9oYV9tZWFuIC0gY291bnRfaGFfc2QsDQogICAgICAgICAgICAgICAgICAgIHltYXggPSBjb3VudF9oYV9tZWFuICsgY291bnRfaGFfc2QpKSArDQogIGZhY2V0X3dyYXAofmNsdXN0ZXIsIHNjYWxlcyA9ICJmcmVlX3kiLCBsYWJlbGxlciA9IGxhYmVsX2JvdGgpICsNCiAgdGhlbWVfYncoKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41LCBoanVzdCA9IDEpKSArDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSBzcGVjaWVzKSwgdmp1c3QgPSAwLjI1LCBjaGVja19vdmVybGFwID0gVFJVRSkgKw0KICBsYWJzKHRpdGxlID0gIlNwZWNpZXMgbWVhbiBzdGVtIGNvdW50IHBlciBjbHVzdGVyIChuL2hhKSBpbiB5ZWFyIDMwIikNCg0KIyB2b2x1bWUvYmlvbWFzcw0KZ2dwbG90KHdfbWVhbnNfY2xpbVt3X21lYW5zX2NsaW0keWVhciA9PSAzMCxdLA0KICAgICAgIGFlcyh4ID0gc3BlY2llcywgeSA9IHZvbHVtZV9tM19tZWFuLCBmaWxsID0gZXh0ZXJuYWxfc2VlZCkpICsNCiAgZ2VvbV9jb2wocG9zaXRpb24gPSAiZG9kZ2UiKSArDQogIGdlb21fZXJyb3JiYXIoYWVzKHltaW4gPSB2b2x1bWVfbTNfbWVhbiAtIHZvbHVtZV9tM19zZCwNCiAgICAgICAgICAgICAgICAgICAgeW1heCA9IHZvbHVtZV9tM19tZWFuICsgdm9sdW1lX20zX3NkKSkgKw0KICBmYWNldF93cmFwKH5jbHVzdGVyLCBzY2FsZXMgPSAiZnJlZV95IiwgbGFiZWxsZXIgPSBsYWJlbF9ib3RoKSArDQogIHRoZW1lX2J3KCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSwgaGp1c3QgPSAxKSkgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gc3BlY2llcyksIHZqdXN0ID0gMC4yNSwgY2hlY2tfb3ZlcmxhcCA9IFRSVUUpICsNCiAgbGFicyh0aXRsZSA9ICJTcGVjaWVzIG1lYW4gdm9sdW1lIHBlciBjbHVzdGVyIChtwrMpL0Jpb21hc3MgaW4geWVhciAzMCIpDQoNCiMgREJIDQpnZ3Bsb3Qod19tZWFuc19jbGltW3dfbWVhbnNfY2xpbSR5ZWFyID09IDMwLF0sDQogICAgICAgYWVzKHggPSBzcGVjaWVzLCB5ID0gZGJoX2F2Z19jbV9tZWFuLCBmaWxsID0gZXh0ZXJuYWxfc2VlZCkpICsNCiAgZ2VvbV9jb2wocG9zaXRpb24gPSAiZG9kZ2UiKSArDQogIGdlb21fZXJyb3JiYXIoYWVzKHltaW4gPSBkYmhfYXZnX2NtX21lYW4gLSBkYmhfYXZnX2NtX3NkLA0KICAgICAgICAgICAgICAgICAgICB5bWF4ID0gZGJoX2F2Z19jbV9tZWFuICsgZGJoX2F2Z19jbV9zZCkpICsNCiAgZmFjZXRfd3JhcCh+Y2x1c3Rlciwgc2NhbGVzID0gImZyZWVfeSIsIGxhYmVsbGVyID0gbGFiZWxfYm90aCkgKw0KICB0aGVtZV9idygpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAwLjUsIGhqdXN0ID0gMSkpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHNwZWNpZXMpLCB2anVzdCA9IDAuMjUsIGNoZWNrX292ZXJsYXAgPSBUUlVFKSArDQogIGxhYnModGl0bGUgPSAiU3BlY2llcyBtZWFuIGRiaCBwZXIgY2x1c3RlciAoY20pIGluIHllYXIgMzAiKQ0KDQojIGJhc2FsIGFyZWENCmdncGxvdCh3X21lYW5zX2NsaW1bd19tZWFuc19jbGltJHllYXIgPT0gMzAsXSwNCiAgICAgICBhZXMoeCA9IHNwZWNpZXMsIHkgPSBiYXNhbF9hcmVhX20yX21lYW4sIGZpbGwgPSBleHRlcm5hbF9zZWVkKSkgKw0KICBnZW9tX2NvbChwb3NpdGlvbiA9ICJkb2RnZSIpICsNCiAgZ2VvbV9lcnJvcmJhcihhZXMoeW1pbiA9IGJhc2FsX2FyZWFfbTJfbWVhbiAtIGJhc2FsX2FyZWFfbTJfc2QsDQogICAgICAgICAgICAgICAgICAgIHltYXggPSBiYXNhbF9hcmVhX20yX21lYW4gKyBiYXNhbF9hcmVhX20yX3NkKSkgKw0KICBmYWNldF93cmFwKH5jbHVzdGVyLCBzY2FsZXMgPSAiZnJlZV95IiwgbGFiZWxsZXIgPSBsYWJlbF9ib3RoKSArDQogIHRoZW1lX2J3KCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSwgaGp1c3QgPSAxKSkgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gc3BlY2llcyksIHZqdXN0ID0gMC4yNSwgY2hlY2tfb3ZlcmxhcCA9IFRSVUUpICsNCiAgbGFicyh0aXRsZSA9ICJTcGVjaWVzIG1lYW4gYmFzYWwgYXJlYSBwZXIgY2x1c3RlciAobcKyKSBpbiB5ZWFyIDMwIikNCg0KIyBjb2hvcnQgY291bnQNCmdncGxvdCh3X21lYW5zX2NsaW1bd19tZWFuc19jbGltJHllYXIgPT0gMzAsXSwNCiAgICAgICBhZXMoeCA9IHNwZWNpZXMsIHkgPSBjb2hvcnRfY291bnRfaGFfbWVhbiwgZmlsbCA9IGV4dGVybmFsX3NlZWQpKSArDQogIGdlb21fY29sKHBvc2l0aW9uID0gImRvZGdlIikgKw0KICBnZW9tX2Vycm9yYmFyKGFlcyh5bWluID0gY29ob3J0X2NvdW50X2hhX21lYW4gLSBjb2hvcnRfY291bnRfaGFfc2QsDQogICAgICAgICAgICAgICAgICAgIHltYXggPSBjb2hvcnRfY291bnRfaGFfbWVhbiArIGNvaG9ydF9jb3VudF9oYV9zZCkpICsNCiAgZmFjZXRfd3JhcCh+Y2x1c3Rlciwgc2NhbGVzID0gImZyZWVfeSIsIGxhYmVsbGVyID0gbGFiZWxfYm90aCkgKw0KICB0aGVtZV9idygpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAwLjUsIGhqdXN0ID0gMSkpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHNwZWNpZXMpLCB2anVzdCA9IDAuMjUsIGNoZWNrX292ZXJsYXAgPSBUUlVFKSArDQogIGxhYnModGl0bGUgPSAiU3BlY2llcyBtZWFuIGNvaG9ydCBjb3VudCBwZXIgY2x1c3RlciAobi9oYSkgaW4geWVhciAzMCIpDQpgYGANCg0KIyMgMy4xIEludGVyZXN0aW5nIHBsb3RzDQoNCkJpb21hc3MgZGV2ZWxvcG1lbnQgYW5kIGJpb21hc3Mgc3RhdGUgaW4geWVhciAzMC4gTWVhbiB2b2x1bWUgcGVyIHNjZW5hcmlvIGFuZCBjbHVzdGVyICh5ID0gbWVhbiB2b2wsIHggPSBjbHVzdGVyKS4NCg0KYGBge3IgaW50ZXJlc3RpbmdfcGxvdHN9DQojIERldmVsb3BtZW50IG9mIGJpb21hc3Mvdm9sdW1lIG92ZXIgdGltZQ0KcGxvdDEgPC0gZ2dwbG90KHdfbWVhbnNfY2xpbSwgYWVzKHggPSB5ZWFyLCB5ID0gdm9sdW1lX20zX21lYW4sIGNvbCA9IHNwZWNpZXMpKSArDQogIGdlb21fbGluZShhZXMobGluZXR5cGUgPSBleHRlcm5hbF9zZWVkKSkgKw0KICBnZW9tX3JpYmJvbihhZXMobGluZXR5cGUgPSBleHRlcm5hbF9zZWVkLA0KICAgICAgICAgICAgICAgICAgeW1pbiA9IHZvbHVtZV9tM19tZWFuIC0gdm9sdW1lX20zX3NkLA0KICAgICAgICAgICAgICAgICAgeW1heCA9IHZvbHVtZV9tM19tZWFuICsgdm9sdW1lX20zX3NkLA0KICAgICAgICAgICAgICAgICAgZmlsbCA9IHNwZWNpZXMpLA0KICAgICAgICAgICAgICAgICAgYWxwaGEgPSAwLjA1KSArDQogIGZhY2V0X3dyYXAofmNsdXN0ZXIsIHNjYWxlcyA9ICJmcmVlIikgKw0KICB0aGVtZV9idygpICsNCiAgbGFicyh0aXRsZSA9ICJEZXZlbG9wbWVudCBvZiBtZWFuIHZvbHVtZShtwrMpL2Jpb21hc3MgcGVyIGNsdXN0ZXIiKQ0KDQojIFZvbHVtZS9iaW9tYXNzIHN0YXRlIGluIHllYXIgMzANCnBsb3QyIDwtIGdncGxvdCh3X21lYW5zX2NsaW1bd19tZWFuc19jbGltJHllYXIgPT0gMzAsXSwNCiAgICAgICBhZXMoeCA9IHNwZWNpZXMsIHkgPSB2b2x1bWVfbTNfbWVhbiwgZmlsbCA9IGV4dGVybmFsX3NlZWQpKSArDQogIGdlb21fY29sKHBvc2l0aW9uID0gImRvZGdlIikgKw0KICBnZW9tX2Vycm9yYmFyKGFlcyh5bWluID0gdm9sdW1lX20zX21lYW4gLSB2b2x1bWVfbTNfc2QsDQogICAgICAgICAgICAgICAgICAgIHltYXggPSB2b2x1bWVfbTNfbWVhbiArIHZvbHVtZV9tM19zZCkpICsNCiAgZmFjZXRfd3JhcCh+Y2x1c3Rlciwgc2NhbGVzID0gImZyZWVfeSIsIGxhYmVsbGVyID0gbGFiZWxfYm90aCkgKw0KICB0aGVtZV9idygpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAwLjUsIGhqdXN0ID0gMSkpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHNwZWNpZXMpLCB2anVzdCA9IDAuMjUsIGNoZWNrX292ZXJsYXAgPSBUUlVFKSArDQogIGxhYnModGl0bGUgPSAiU3BlY2llcyBtZWFuIHZvbHVtZSBwZXIgY2x1c3RlciAobcKzKS9CaW9tYXNzIGluIHllYXIgMzAiKQ0KDQojIE1lYW4gdm9sdW1lIHBlciBzY2VuYXJpbyBhbmQgY2x1c3RlciBpbiB5ZWFyIDMwDQpwbG90MyA8LSBnZ3Bsb3Qod19tZWFuc19jbGltW3dfbWVhbnNfY2xpbSR5ZWFyID09IDMwLF0sDQogICAgICAgYWVzKHggPSBjbHVzdGVyLCB5ID0gdm9sdW1lX20zX21lYW4sIGZpbGwgPSBzcGVjaWVzKSkgKw0KICBnZW9tX2NvbChjb2wgPSAiYmxhY2siKSArDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSBzcGVjaWVzKSwNCiAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2sodmp1c3QgPSAwLjUpLA0KICAgICAgICAgICAgc2l6ZSA9IDQsDQogICAgICAgICAgICBjaGVja19vdmVybGFwID0gVFJVRSkgKw0KICBmYWNldF93cmFwKH5leHRlcm5hbF9zZWVkLCBzY2FsZXMgPSAiZnJlZSIsIGxhYmVsbGVyID0gbGFiZWxfYm90aCkgKw0KICB0aGVtZV9idygpICsNCiAgbGFicyh0aXRsZSA9ICJNZWFuIHZvbHVtZS9iaW9tYXNzIHBlciBjbHVzdGVyIGluIHllYXIgMzAiKQ0KDQpwbG90MQ0KcGxvdDINCnBsb3QzDQpgYGANCg0KIyMgMy4yIFNhdmUgcGxvdHMNCg0KU2FmZSB0aGUgMyBwbG90cyBhcyAucG5nIGZpbGUuDQoNCmBgYHtyfQ0KcG5nKCJwbG90cy9wbG90MV95ZWFyLXZvbC5wbmciLCB3aWR0aCA9IDEyMDAsIGhlaWdodCA9IDEyMDApDQpwbG90MQ0KZGV2Lm9mZigpDQoNCnBuZygicGxvdHMvcGxvdDJfc3BlY2llcy12b2wucG5nIiwgd2lkdGggPSAxMjAwLCBoZWlnaHQgPSAxMjAwKQ0KcGxvdDINCmRldi5vZmYoKQ0KDQpwbmcoInBsb3RzL3Bsb3QzX2NsdXN0ZXItdm9sLnBuZyIsIHdpZHRoID0gMTIwMCwgaGVpZ2h0ID0gMTIwMCkNCnBsb3QzDQpkZXYub2ZmKCkNCmBgYA0KDQojIDQuIERlcml2ZSBzdGFuZCBwYXJhbWV0ZXINCg0KVGhlIGRldmVsb3BtZW50IG9mIHRoZSBzdGFuZCBjYW4gYWxzbyBiZSBwcmVkaWN0ZWQgdXNpbmcgNiBpbmRpY2F0b3JzLCA0IG9mIHRob3NlIHNob3VsZCBiZSBjYWxjdWxhdGVkIHdpdGggaUxhbmQgZGF0YSAodHJlZSBzcGVjaWVzLCBiYXNhbCBhcmVhLCBzdGVtIGRlbnNpdHksIHZlcnRpY2FsIGNsYXNzKS4NCg0KYGBge3IgY2FsY19wYXJhbWV0ZXJzfQ0Kd19tZWFuc19zYXBsX2NsaW0kZXh0ZXJuYWxfc2VlZCA8LSBGQUxTRQ0Kd19tZWFuc19zYXBsX2NsaW1bd19tZWFuc19zYXBsX2NsaW0kZXh0X3NlZWQgPT0gInNlZWQiLF0kZXh0ZXJuYWxfc2VlZCA8LSBUUlVFDQp3X21lYW5zX3NhcGxfY2xpbSA8LSBkcGx5cjo6c2VsZWN0KHdfbWVhbnNfc2FwbF9jbGltLCAhZXh0X3NlZWQpDQoNCndfbWVhbnNfc2FwRF9jbGltJGV4dGVybmFsX3NlZWQgPC0gRkFMU0UNCndfbWVhbnNfc2FwRF9jbGltW3dfbWVhbnNfc2FwRF9jbGltJGV4dF9zZWVkID09ICJzZWVkIixdJGV4dGVybmFsX3NlZWQgPC0gVFJVRQ0Kd19tZWFuc19zYXBEX2NsaW0gPC0gZHBseXI6OnNlbGVjdCh3X21lYW5zX3NhcERfY2xpbSwgIWV4dF9zZWVkKQ0KDQojIEdhdGhlciBkYXRhIGZvciBldmVyeSBjbHVzdGVyDQp3X2luZGljIDwtIGlubmVyX2pvaW4od19tZWFuc19jbGltLCB3X21lYW5zX3NhcGxfY2xpbSwNCiAgICAgICAgICAgICAgICAgICAgICBieSA9IGpvaW5fYnkoZXh0ZXJuYWxfc2VlZCwgY2x1c3RlciwgeWVhciwgc3BlY2llcykpDQoNCndfaW5kaWMgPC0gd19pbmRpYyAlPiUNCiAgZHBseXI6OnJlbmFtZShjb3VudF9oYV80X21lYW4gPSBjb3VudF9oYV9tZWFuLngsICMgdHJlZSBjb3VudC9oYSAoPjRtIGhlaWdodCkNCiAgICAgICAgICAgICAgICBjb3VudF9oYV80X3NkID0gY291bnRfaGFfc2QueCwNCiAgICAgICAgICAgICAgICBjb19jbnRfaGFfbWVhbiA9IGNvaG9ydF9jb3VudF9oYV9tZWFuLngsICMgbnVtYmVyIG9mIGNvaG9ydHMgKDw0bSBoZWlnaHQpDQogICAgICAgICAgICAgICAgY29fY250X2hhX3NkID0gY29ob3J0X2NvdW50X2hhX3NkLngsICMgbnVtYmVyIG9mIGNvaG9ydHMgKDw0bSBoZWlnaHQpDQogICAgICAgICAgICAgICAgY250X2hhXzEzX21lYW4gPSBjb3VudF9oYV9tZWFuLnksICMgbnVtYmVyIG9mIGluZGl2aWR1YWxzL2hhICg+MS4zbSBoZWlnaHQpDQogICAgICAgICAgICAgICAgY250X2hhXzEzX3NkID0gY291bnRfaGFfc2QueSAjIG51bWJlciBvZiBpbmRpdmlkdWFscy9oYSAoPjEuM20gaGVpZ2h0KQ0KICAgICAgICAgICAgICApDQp3X2luZGljIDwtIGlubmVyX2pvaW4od19pbmRpYywgd19tZWFuc19zYXBEX2NsaW0sDQogICAgICAgICAgICAgICAgICAgICAgYnkgPSBqb2luX2J5KGV4dGVybmFsX3NlZWQsIGNsdXN0ZXIsIHllYXIsIHNwZWNpZXMpKQ0Kd19pbmRpYyAlPiUNCiAgZHBseXI6OnJlbmFtZShuX3JlcHJlc2VudGVkX2NvaG9fbWVhbiA9IG5fcmVwcmVzZW50ZWRfbWVhbiwNCiAgICAgICAgICAgICAgICBuX3JlcHJlc2VudGVkX2NvaG9fc2QgPSBuX3JlcHJlc2VudGVkX3NkLA0KICAgICAgICAgICAgICAgIGNvaG9fZGJoX21lYW4gPSBkYmhfbWVhbiwNCiAgICAgICAgICAgICAgICBjb2hvX2RiaF9zZCA9IGRiaF9zZCwNCiAgICAgICAgICAgICAgICBjb2hvX2FnZV9tZWFuID0gYWdlX21lYW4sDQogICAgICAgICAgICAgICAgY29ob19hZ2Vfc2QgPSBhZ2Vfc2QpDQoNCmBgYA0KDQpEZXNjcmlwdGlvbiBvZiBjb2x1bW5zOg0KDQp8IENvbHVtbiBuYW1lICAgICAgICAgICAgfCBEZXNjcmlwdGlvbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18DQp8IGV4dGVybmFsX3NlZWQgICAgICAgICAgfCBXaGVhdGhlciBleHRlcm5hbCBzZWVkIHdhcyBjb25zaWRlcmVkIGluIHRoZSBzaW11bGF0aW9uICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBjbHVzdGVyICAgICAgICAgICAgICAgIHwgVG8gd2hpY2ggb2YgdGhlIDEyIGNsdXN0ZXJzIHRoaXMgbGluZSBiZWxvbmdzICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgeWVhciAgICAgICAgICAgICAgICAgICB8IHNpbXVsYXRlZCB5ZWFyICgwIGlzIGluaXRpYWwgc3RhdGUsIDMwIGZpbmFsIHN0YXRlKSAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IHNwZWNpZXMgICAgICAgICAgICAgICAgfCB0cmVlIHNwZWNpZXMgdGhlIGRhdGEgcmVsYXRlcyB0byAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCB2b2x1bWVfbTNfbWVhbiAgICAgICAgIHwgdm9sdW1lIG9mIHNwZWNpZXMgYmlvbWFzcyAobWVhbikgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgdm9sdW1lX20zX3NkICAgICAgICAgICB8IHZvbHVtZSBvZiBzcGVjaWVzIGJpb21hc3MgKHN0YW5kYXJkIGRldmlhdGlvbikgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IGRiaF9hdmdfY21fbWVhbiAgICAgICAgfCBhdmVyYWdlIGRiaCBvZiB0cmVlcyAoXD40bSBoZWlnaHQpIChtZWFuKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBkYmhfYXZnX2NtX3NkICAgICAgICAgIHwgYXZlcmFnZSBkYmggb2YgdHJlZXMgKFw+NG0gaGVpZ2h0KSAoc3RhbmRhcmQgZGV2aWF0aW9uKSAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgYmFzYWxfYXJlYV9tMl9tZWFuICAgICB8IHRvdGFsIGJhc2FsIGFyZWEgYXQgYnJlYXN0IGhlaWdodCAoXD40bSBoZWlnaHQpIChtZWFuKSAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IGJhc2FsX2FyZWFfbTJfc2QgICAgICAgfCB0b3RhbCBiYXNhbCBhcmVhIGF0IGJyZWFzdCBoZWlnaHQgKFw+NG0gaGVpZ2h0KSAoc3RhbmRhcmQgZGV2aWF0aW9uKSAgICAgICAgICAgfA0KfCBjb3VudF9oYV80X21lYW4gICAgICAgIHwgdHJlZSBjb3VudCAobGl2aW5nLCBcPjRtIGhlaWdodCkgcGVyIGhhIChtZWFuKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgY291bnRfaGFfNF9zZCAgICAgICAgICB8IHRyZWUgY291bnQgKGxpdmluZywgXD40bSBoZWlnaHQpIHBlciBoYSAoc3RhbmRhcmQgZGV2aWF0aW9uKSAgICAgICAgICAgICAgICAgICB8DQp8IGNvX2NudF9oYV9tZWFuICAgICAgICAgfCBudW1iZXIgb2YgY29ob3J0cyBpbiB0aGUgcmVnZW5lcmF0aW9uIGxheWVyIChcPDRtKSBwZXIgaGEgKG1lYW4pICAgICAgICAgICAgICAgfA0KfCBjb19jbnRfaGFfc2QgICAgICAgICAgIHwgbnVtYmVyIG9mIGNvaG9ydHMgaW4gdGhlIHJlZ2VuZXJhdGlvbiBsYXllciAoXDw0bSkgcGVyIGhhIChzdGFuZGFyZCBkZXZpYXRpb24pIHwNCnwgY29ob3J0X2Jhc2FsX2FyZWFfbWVhbiB8IGJhc2FsIGFyZWEgb2Ygc2FwbGluZ3MgKFw+MS4zbSBhbmQgXDw0bSkgKG1lYW4pICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IGNvaG9ydF9iYXNhbF9hcmVhX3NkICAgfCBiYXNhbCBhcmVhIG9mIHNhcGxpbmdzIChcPjEuM20gYW5kIFw8NG0pIChzdGFuZGFyZCBkZXZpYXRpb24pICAgICAgICAgICAgICAgICAgfA0KfCBjbnRfaGFfMTNfbWVhbiAgICAgICAgIHwgbnVtYmVyIG9mIHJlcHJlc2VudGVkIGluZGl2aWR1YWxzIHBlciBoYSAoXD4xLjNtIGhlaWdodCkgKG1lYW4pICAgICAgICAgICAgICAgIHwNCnwgY250X2hhXzEzX3NkICAgICAgICAgICB8IG51bWJlciBvZiByZXByZXNlbnRlZCBpbmRpdmlkdWFscyBwZXIgaGEgKFw+MS4zbSBoZWlnaHQpIChzdGFuZGFyZCBkZXZpYXRpb24pICB8DQp8IGNvdW50X3NtYWxsX2hhX21lYW4gICAgfCBudW1iZXIgb2YgcmVwcmVzZW50ZWQgaW5kaXZpZHVhbHMgcGVyIGhhIChcPD0xLjNtIGhlaWdodCkgKG1lYW4pICAgICAgICAgICAgICAgfA0KfCBjb3VudF9zbWFsbF9oYV9zZCAgICAgIHwgbnVtYmVyIG9mIHJlcHJlc2VudGVkIGluZGl2aWR1YWxzIHBlciBoYSAoXDw9MS4zbSBoZWlnaHQpIChzdGFuZGFyZCBkZXZpYXRpb24pIHwNCnwgY29ob3J0X2NvdW50X2hhX21lYW4ueSB8IG51bWJlciBvZiBjb2hvcnRzIHBlciBoYSAoc2FtZSBhcyBjb19jbnRfaGFfbWVhbikgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IGNvaG9ydF9jb3VudF9oYV9zZC55ICAgfCBudW1iZXIgb2YgY29ob3J0cyBwZXIgaGEgKHNhbWUgYXMgY29fY250X2hhX3NkKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBoZWlnaHRfYXZnX21fbWVhbiAgICAgIHwgYXJpdGhtZXRpYyBhdmVyYWdlIGhlaWdodCBvZiB0aGUgY29ob3J0cyAobSkgKG1lYW4pICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgaGVpZ2h0X2F2Z19tX3NkICAgICAgICB8IGFyaXRobWV0aWMgYXZlcmFnZSBoZWlnaHQgb2YgdGhlIGNvaG9ydHMgKG0pIChzZCkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IGFnZV9hdmdfbWVhbiAgICAgICAgICAgfCBhcml0aG1ldHJpYyBhdmVyYWdlIGFnZSBvZiB0aGUgc2FwbGluZyBjb2hvcnRzICh5ZWFycykgKG1lYW4pICAgICAgICAgICAgICAgICAgfA0KfCBhZ2VfYXZnX3NkICAgICAgICAgICAgIHwgYXJpdGhtZXRyaWMgYXZlcmFnZSBhZ2Ugb2YgdGhlIHNhcGxpbmcgY29ob3J0cyAoeWVhcnMpIChzdGFuZGFyZCBkZXZpYWlvbikgICAgIHwNCnwgYWdlX2F2Z19zZCAgICAgICAgICAgICB8IGFyaXRobWV0cmljIGF2ZXJhZ2UgYWdlIG9mIHRoZSBzYXBsaW5nIGNvaG9ydHMgKHllYXJzKSAoc3RhbmRhcmQgZGV2aWFpb24pICAgICB8DQp8IG5fcmVwcmVzZW50ZWRfbWVhbiAgICAgfCBudW1iZXIgb2YgdHJlZXMgdGhhdCBhcmUgcmVwcmVzZW50ZWQgYnkgdGhlIGNvaG9ydCAoUmVpbmVrZSBmdW5jdGlvbikgKG1lYW4pICAgfA0KfCBuX3JlcHJlc2VudGVkX3NkICAgICAgIHwgbnVtYmVyIG9mIHRyZWVzIHRoYXQgYXJlIHJlcHJlc2VudGVkIGJ5IHRoZSBjb2hvcnQgKFJlaW5la2UgZnVuY3Rpb24pIChzZCkgICAgIHwNCnwgZGJoX21lYW4gICAgICAgICAgICAgICB8IGRpYW1ldGVyIG9mIHRoZSBjb2hvcnQgaW4gY20gKG1lYW4pICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IGRiaF9zZCAgICAgICAgICAgICAgICAgfCBkaWFtZXRlciBvZiB0aGUgY29ob3J0IGluIGNtIChzZCkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBoZWlnaHRfbWVhbiAgICAgICAgICAgIHwgaGVpZ2h0IG9mIHRoZSBjb2hvcnQgaW4gbSAobWVhbikgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgaGVpZ2h0X3NkICAgICAgICAgICAgICB8IGhlaWdodCBvZiB0aGUgY29ob3J0IGluIG0gKHNkKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IGFnZV9tZWFuICAgICAgICAgICAgICAgfCBhZ2Ugb2YgdGhlIGNvaG9ydCAobWVhbikgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBhZ2Vfc2QgICAgICAgICAgICAgICAgIHwgYWdlIG9mIHRoZSBjb2hvcnQgKHNkKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCg0KOiBjb2x1bW4gZGVzY3JpcHRpb25zDQo=